/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.node;

import java.util.Collection;
import java.util.Map;

import jp.aist.gtrc.plus.reserve.ReserveException;
import jp.aist.gtrc.plus.reserve.ReserveRequest;
import jp.aist.gtrc.plus.scheduler.status.ArchType;
import jp.aist.gtrc.plus.scheduler.status.NodeStatus;
import jp.aist.gtrc.plus.scheduler.status.OSType;
import jp.aist.gtrc.plus.scheduler.util.CollectionUtil;

/*
 *  NodeResource is used by both NodeInfo and JobStatus to express the
 *    resource in the physical node and resource required by the job, 
 *    respectively.
 *  BUG: NodeResource for JobStatus doesn't contains required resource 
 *    properties but for NodeStatus does.  So, it cannot be used to match node
 *    and job on resource properties.
 */
public class NodeResource extends ResourceOption {

	private static final long serialVersionUID = -4656471612748213735L;

	public NodeResource(NodeStatus status){

		this(status.getCPUNum(), 1, status.getArchType(), status.getOSType());

		try {
			setNodePhysicalMemory(status.getPhysicalMemory());
		} catch (ReserveException e) {
			throw new NumberFormatException(e.getMessage());
		}
		addResourceOptions(status.getAttributeMap());

	}

	public NodeResource(int cpunum, int procnum, ArchType arch, OSType os) {

		super();

		try {
			setNodeCPUNum(cpunum);
			setProcessNum(procnum);
		} catch (ReserveException e) {
			throw new NumberFormatException(e.getMessage());
		}
		setArchType(arch);
		setOSType(os);

	}

	public NodeResource(int cpunum, int procnum) {

		this(cpunum, procnum, ArchType.unknown, OSType.Unknown);

	}

	public NodeResource(int cpunum) {

		this(cpunum, cpunum);

	}

	public NodeResource(ReserveRequest rsvReq){

		super();
		reloadOptions(rsvReq.getOptions());

	}

	protected String getCanonicalResourceName(String name) {

		/*
		 * NOTE: On SGE, canonical name and shortcut name are usable.
		 */
		return name;

	}

	protected boolean isResourceMatch(String rscName, String reqValue, String myValue) {

		return (reqValue.equals(myValue));
	}

	protected boolean isExtendResourceEnough(NodeResource reqResource) {

		Map<String, String> reqMap = reqResource.getResourceOptions();
		Map<String, String> myMap = this.getResourceOptions();

		if (CollectionUtil.isEmpty(reqMap)) {
			// nothing requested
			return true;
		}
		if (CollectionUtil.isEmpty(myMap)) {
			// requested, but I don't have any resource
			return false;
		}

		for (Map.Entry<String, String> entry : reqMap.entrySet()) {
			String key = getCanonicalResourceName(entry.getKey());
			String myValue = myMap.get(key);
			if (myValue == null) {
				// don't have requested resource
				return false;
			}
			if (isResourceMatch(key, entry.getValue(), myValue) == false) {
				// don't match resource value
				return false;
			}
		}
                System.out.println("    isExtendResourceEnough() OK");

		// I have all of requested resources
		return true;

	}

        /*
         * We correct the enough resource checking to take into account the
         * current status of the node due to some resources are allocated
         * to previous jobs already. However, we can check only CPU num.
         * Note that reqRsc.getNodeCPUNum() == ncpus but
         *           nodeStatus.getNodeCPUNum() == np
         * - Ekasit 
         */
	private boolean isResourceEnough(NodeStatus nodeStatus, NodeResource reqRsc) {

		// enough CPU (dual or SMP etc) ?
                // 
		if (this.getNodeCPUNum() < reqRsc.getNodeCPUNum()) {
System.out.println("but this node has not enough CPUs to satisfy ncpus " + reqRsc.getNodeCPUNum());
			return false;
                }

                int remainingSlotNum = this.getNodeCPUNum() - nodeStatus.getRunningJobNum();
		if (remainingSlotNum < reqRsc.getProcessNum()) {
System.out.println("but this node has remaining " + remainingSlotNum + " slots < reqRsc.getProcessNum() " + reqRsc.getProcessNum());
			return false;
                }

		// enough Memory ?
		if (this.getNodePhysicalMemory() < reqRsc.getNodePhysicalMemory()) {
System.out.println("but this node has memory " + this.getNodePhysicalMemory() + " < reqRsc.getNodePhysicalMemory() " + reqRsc.getNodePhysicalMemory());
			return false;
                }

		ArchType reqArch = reqRsc.getArchType();
		if ((reqArch != null) && (reqArch != ArchType.unknown)) {
			if (this.getArchType() != reqArch) {
System.out.println("but this.getArchType() " + this.getArchType() + " != reqArch" + reqArch);
				return false;
                        }
		} else {
			// means requested arch not specified, any arch is OK
		}

		OSType reqOS = reqRsc.getOSType();
		if ((reqOS != null) && (reqOS != OSType.Unknown)) {
			if (OSType.isMatch(reqOS, this.getOSType()) == false) {
System.out.println("reqOs " + reqOS + " ==  this.getOSType() " + this.getOSType());
				return false;
			}
		} else {
			// means requested OS not specified, any OS is OK
		}

		return isExtendResourceEnough(reqRsc);

	}

	public NodeResource getEnoughResource(NodeStatus nodeStatus, Collection<NodeResource> reqResources) {

		for (NodeResource rsc : reqResources) {
			if (isResourceEnough(nodeStatus, rsc) == true) {
System.out.println("    isResourceEnough() OK");
				return rsc;
                        }
		}

		return null;

	}

	public boolean isEnough(NodeStatus nodeStatus, Collection<NodeResource> reqResources) {

		return (getEnoughResource(nodeStatus, reqResources) != null);

	}

}
